Skip to content

Commit c329f73

Browse files
authored
Merge branch 'openai:main' into main
2 parents c0b91ed + 9f828cb commit c329f73

File tree

12 files changed

+262
-50
lines changed

12 files changed

+262
-50
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.3.3"
2+
".": "1.3.5"
33
}

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
# Changelog
22

3+
## 1.3.5 (2023-11-21)
4+
5+
Full Changelog: [v1.3.4...v1.3.5](https://github.com/openai/openai-python/compare/v1.3.4...v1.3.5)
6+
7+
### Bug Fixes
8+
9+
* **azure:** ensure custom options can be passed to copy ([#858](https://github.com/openai/openai-python/issues/858)) ([05ca0d6](https://github.com/openai/openai-python/commit/05ca0d68e84d40f975614d27cb52c0f382104377))
10+
11+
12+
### Chores
13+
14+
* **package:** add license classifier ([#826](https://github.com/openai/openai-python/issues/826)) ([bec004d](https://github.com/openai/openai-python/commit/bec004d030b277e05bdd51f66fae1e881291c30b))
15+
* **package:** add license classifier metadata ([#860](https://github.com/openai/openai-python/issues/860)) ([80dffb1](https://github.com/openai/openai-python/commit/80dffb17ff0a10b0b9ea704c4247521e48b68408))
16+
17+
## 1.3.4 (2023-11-21)
18+
19+
Full Changelog: [v1.3.3...v1.3.4](https://github.com/openai/openai-python/compare/v1.3.3...v1.3.4)
20+
21+
### Bug Fixes
22+
23+
* **client:** attempt to parse unknown json content types ([#854](https://github.com/openai/openai-python/issues/854)) ([ba50466](https://github.com/openai/openai-python/commit/ba5046611029a67714d5120b9cc6a3c7fecce10c))
24+
25+
26+
### Chores
27+
28+
* **examples:** fix static types in assistants example ([#852](https://github.com/openai/openai-python/issues/852)) ([5b47b2c](https://github.com/openai/openai-python/commit/5b47b2c542b9b4fb143af121022e2d5ad0890ef4))
29+
330
## 1.3.3 (2023-11-17)
431

532
Full Changelog: [v1.3.2...v1.3.3](https://github.com/openai/openai-python/compare/v1.3.2...v1.3.3)

examples/assistant.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import openai
21
import time
32

3+
import openai
4+
45
# gets API Key from environment variable OPENAI_API_KEY
56
client = openai.OpenAI()
67

@@ -16,38 +17,31 @@
1617
message = client.beta.threads.messages.create(
1718
thread_id=thread.id,
1819
role="user",
19-
content="I need to solve the equation `3x + 11 = 14`. Can you help me?"
20+
content="I need to solve the equation `3x + 11 = 14`. Can you help me?",
2021
)
2122

2223
run = client.beta.threads.runs.create(
23-
thread_id=thread.id,
24-
assistant_id=assistant.id,
25-
instructions="Please address the user as Jane Doe. The user has a premium account."
24+
thread_id=thread.id,
25+
assistant_id=assistant.id,
26+
instructions="Please address the user as Jane Doe. The user has a premium account.",
2627
)
2728

2829
print("checking assistant status. ")
2930
while True:
30-
run = client.beta.threads.runs.retrieve(
31-
thread_id=thread.id,
32-
run_id=run.id
33-
)
31+
run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)
3432

3533
if run.status == "completed":
3634
print("done!")
37-
messages = client.beta.threads.messages.list(
38-
thread_id=thread.id
39-
)
35+
messages = client.beta.threads.messages.list(thread_id=thread.id)
4036

4137
print("messages: ")
4238
for message in messages:
43-
print({
44-
"role": message.role,
45-
"message": message.content[0].text.value
46-
})
39+
assert message.content[0].type == "text"
40+
print({"role": message.role, "message": message.content[0].text.value})
4741

4842
client.beta.assistants.delete(assistant.id)
49-
43+
5044
break
5145
else:
5246
print("in progress...")
53-
time.sleep(5)
47+
time.sleep(5)

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "openai"
3-
version = "1.3.3"
3+
version = "1.3.5"
44
description = "The official Python library for the openai API"
55
readme = "README.md"
66
license = "Apache-2.0"
@@ -32,6 +32,7 @@ classifiers = [
3232
"Operating System :: POSIX :: Linux",
3333
"Operating System :: Microsoft :: Windows",
3434
"Topic :: Software Development :: Libraries :: Python Modules",
35+
"License :: OSI Approved :: Apache Software License"
3536
]
3637

3738
[project.optional-dependencies]

src/openai/_base_client.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,12 @@
7474
RAW_RESPONSE_HEADER,
7575
)
7676
from ._streaming import Stream, AsyncStream
77-
from ._exceptions import APIStatusError, APITimeoutError, APIConnectionError
77+
from ._exceptions import (
78+
APIStatusError,
79+
APITimeoutError,
80+
APIConnectionError,
81+
APIResponseValidationError,
82+
)
7883

7984
log: logging.Logger = logging.getLogger(__name__)
8085

@@ -518,13 +523,16 @@ def _process_response_data(
518523
if cast_to is UnknownResponse:
519524
return cast(ResponseT, data)
520525

521-
if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol):
522-
return cast(ResponseT, cast_to.build(response=response, data=data))
526+
try:
527+
if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol):
528+
return cast(ResponseT, cast_to.build(response=response, data=data))
523529

524-
if self._strict_response_validation:
525-
return cast(ResponseT, validate_type(type_=cast_to, value=data))
530+
if self._strict_response_validation:
531+
return cast(ResponseT, validate_type(type_=cast_to, value=data))
526532

527-
return cast(ResponseT, construct_type(type_=cast_to, value=data))
533+
return cast(ResponseT, construct_type(type_=cast_to, value=data))
534+
except pydantic.ValidationError as err:
535+
raise APIResponseValidationError(response=response, body=data) from err
528536

529537
@property
530538
def qs(self) -> Querystring:

src/openai/_client.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import os
66
import asyncio
7-
from typing import Union, Mapping
8-
from typing_extensions import override
7+
from typing import Any, Union, Mapping
8+
from typing_extensions import Self, override
99

1010
import httpx
1111

@@ -164,12 +164,10 @@ def copy(
164164
set_default_headers: Mapping[str, str] | None = None,
165165
default_query: Mapping[str, object] | None = None,
166166
set_default_query: Mapping[str, object] | None = None,
167-
) -> OpenAI:
167+
_extra_kwargs: Mapping[str, Any] = {},
168+
) -> Self:
168169
"""
169170
Create a new client instance re-using the same options given to the current client with optional overriding.
170-
171-
It should be noted that this does not share the underlying httpx client class which may lead
172-
to performance issues.
173171
"""
174172
if default_headers is not None and set_default_headers is not None:
175173
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
@@ -199,6 +197,7 @@ def copy(
199197
max_retries=max_retries if is_given(max_retries) else self.max_retries,
200198
default_headers=headers,
201199
default_query=params,
200+
**_extra_kwargs,
202201
)
203202

204203
# Alias for `copy` for nicer inline usage, e.g.
@@ -374,12 +373,10 @@ def copy(
374373
set_default_headers: Mapping[str, str] | None = None,
375374
default_query: Mapping[str, object] | None = None,
376375
set_default_query: Mapping[str, object] | None = None,
377-
) -> AsyncOpenAI:
376+
_extra_kwargs: Mapping[str, Any] = {},
377+
) -> Self:
378378
"""
379379
Create a new client instance re-using the same options given to the current client with optional overriding.
380-
381-
It should be noted that this does not share the underlying httpx client class which may lead
382-
to performance issues.
383380
"""
384381
if default_headers is not None and set_default_headers is not None:
385382
raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
@@ -409,6 +406,7 @@ def copy(
409406
max_retries=max_retries if is_given(max_retries) else self.max_retries,
410407
default_headers=headers,
411408
default_query=params,
409+
**_extra_kwargs,
412410
)
413411

414412
# Alias for `copy` for nicer inline usage, e.g.

src/openai/_models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,19 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
263263
return construct_type(value=value, type_=type_)
264264

265265

266+
def is_basemodel(type_: type) -> bool:
267+
"""Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`"""
268+
origin = get_origin(type_) or type_
269+
if is_union(type_):
270+
for variant in get_args(type_):
271+
if is_basemodel(variant):
272+
return True
273+
274+
return False
275+
276+
return issubclass(origin, BaseModel) or issubclass(origin, GenericModel)
277+
278+
266279
def construct_type(*, value: object, type_: type) -> object:
267280
"""Loose coercion to the expected type with construction of nested values.
268281

src/openai/_response.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
from __future__ import annotations
22

33
import inspect
4+
import logging
45
import datetime
56
import functools
67
from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast
78
from typing_extensions import Awaitable, ParamSpec, get_args, override, get_origin
89

910
import httpx
10-
import pydantic
1111

1212
from ._types import NoneType, UnknownResponse, BinaryResponseContent
1313
from ._utils import is_given
14-
from ._models import BaseModel
14+
from ._models import BaseModel, is_basemodel
1515
from ._constants import RAW_RESPONSE_HEADER
1616
from ._exceptions import APIResponseValidationError
1717

@@ -23,6 +23,8 @@
2323
P = ParamSpec("P")
2424
R = TypeVar("R")
2525

26+
log: logging.Logger = logging.getLogger(__name__)
27+
2628

2729
class APIResponse(Generic[R]):
2830
_cast_to: type[R]
@@ -174,6 +176,18 @@ def _parse(self) -> R:
174176
# in the response, e.g. application/json; charset=utf-8
175177
content_type, *_ = response.headers.get("content-type").split(";")
176178
if content_type != "application/json":
179+
if is_basemodel(cast_to):
180+
try:
181+
data = response.json()
182+
except Exception as exc:
183+
log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc)
184+
else:
185+
return self._client._process_response_data(
186+
data=data,
187+
cast_to=cast_to, # type: ignore
188+
response=response,
189+
)
190+
177191
if self._client._strict_response_validation:
178192
raise APIResponseValidationError(
179193
response=response,
@@ -188,14 +202,11 @@ def _parse(self) -> R:
188202

189203
data = response.json()
190204

191-
try:
192-
return self._client._process_response_data(
193-
data=data,
194-
cast_to=cast_to, # type: ignore
195-
response=response,
196-
)
197-
except pydantic.ValidationError as err:
198-
raise APIResponseValidationError(response=response, body=data) from err
205+
return self._client._process_response_data(
206+
data=data,
207+
cast_to=cast_to, # type: ignore
208+
response=response,
209+
)
199210

200211
@override
201212
def __repr__(self) -> str:

src/openai/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless.
22

33
__title__ = "openai"
4-
__version__ = "1.3.3" # x-release-please-version
4+
__version__ = "1.3.5" # x-release-please-version

0 commit comments

Comments
 (0)