Commit 1c59013a
Changed files (5)
src
openai
src/openai/resources/audio/transcriptions.py
@@ -108,11 +108,10 @@ class Transcriptions(SyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
"/audio/transcriptions",
body=maybe_transform(body, transcription_create_params.TranscriptionCreateParams),
@@ -205,11 +204,10 @@ class AsyncTranscriptions(AsyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
"/audio/transcriptions",
body=await async_maybe_transform(body, transcription_create_params.TranscriptionCreateParams),
src/openai/resources/audio/translations.py
@@ -93,11 +93,10 @@ class Translations(SyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
"/audio/translations",
body=maybe_transform(body, translation_create_params.TranslationCreateParams),
@@ -175,11 +174,10 @@ class AsyncTranslations(AsyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
"/audio/translations",
body=await async_maybe_transform(body, translation_create_params.TranslationCreateParams),
src/openai/resources/files.py
@@ -111,11 +111,10 @@ class Files(SyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
"/files",
body=maybe_transform(body, file_create_params.FileCreateParams),
@@ -394,11 +393,10 @@ class AsyncFiles(AsyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["file"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
"/files",
body=await async_maybe_transform(body, file_create_params.FileCreateParams),
src/openai/resources/images.py
@@ -95,11 +95,10 @@ class Images(SyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["image"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
"/images/variations",
body=maybe_transform(body, image_create_variation_params.ImageCreateVariationParams),
@@ -179,11 +178,10 @@ class Images(SyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return self._post(
"/images/edits",
body=maybe_transform(body, image_edit_params.ImageEditParams),
@@ -343,11 +341,10 @@ class AsyncImages(AsyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["image"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
"/images/variations",
body=await async_maybe_transform(body, image_create_variation_params.ImageCreateVariationParams),
@@ -427,11 +424,10 @@ class AsyncImages(AsyncAPIResource):
}
)
files = extract_files(cast(Mapping[str, object], body), paths=[["image"], ["mask"]])
- if files:
- # It should be noted that the actual Content-Type header that will be
- # sent to the server will contain a `boundary` parameter, e.g.
- # multipart/form-data; boundary=---abc--
- extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
return await self._post(
"/images/edits",
body=await async_maybe_transform(body, image_edit_params.ImageEditParams),
src/openai/_base_client.py
@@ -58,6 +58,7 @@ from ._types import (
HttpxSendArgs,
AsyncTransport,
RequestOptions,
+ HttpxRequestFiles,
ModelBuilderProtocol,
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
@@ -460,6 +461,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
headers = self._build_headers(options)
params = _merge_mappings(self.default_query, options.params)
content_type = headers.get("Content-Type")
+ files = options.files
# If the given Content-Type header is multipart/form-data then it
# has to be removed so that httpx can generate the header with
@@ -473,7 +475,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
headers.pop("Content-Type")
# As we are now sending multipart/form-data instead of application/json
- # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding
+ # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding
if json_data:
if not is_dict(json_data):
raise TypeError(
@@ -481,6 +483,15 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
)
kwargs["data"] = self._serialize_multipartform(json_data)
+ # httpx determines whether or not to send a "multipart/form-data"
+ # request based on the truthiness of the "files" argument.
+ # This gets around that issue by generating a dict value that
+ # evaluates to true.
+ #
+ # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186
+ if not files:
+ files = cast(HttpxRequestFiles, ForceMultipartDict())
+
# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
@@ -493,7 +504,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
json=json_data,
- files=options.files,
+ files=files,
**kwargs,
)
@@ -1891,6 +1902,11 @@ def make_request_options(
return options
+class ForceMultipartDict(Dict[str, None]):
+ def __bool__(self) -> bool:
+ return True
+
+
class OtherPlatform:
def __init__(self, name: str) -> None:
self.name = name