Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.16.0"
".": "2.17.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 137
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-506f44f37cccac43899267dd64cc5615e96f6e15f2736aa37e5e4eed2eccc567.yml
openapi_spec_hash: d242c25afd700d928787a46e7901fa45
config_hash: ad7136f7366fddec432ec378939e58a7
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-64c3a646eb5dcad2b7ff7bd976c0e312b886676a542f6ffcd9a6c8503ae24c58.yml
openapi_spec_hash: 91b1b7bf3c1a6b6c9c7507d4cac8fe2a
config_hash: f8e6baff429cf000b8e4ba1da08dff47
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 2.17.0 (2026-02-05)

Full Changelog: [v2.16.0...v2.17.0](https://github.com/openai/openai-python/compare/v2.16.0...v2.17.0)

### Features

* **api:** add shell_call_output status field ([1bbaf88](https://github.com/openai/openai-python/commit/1bbaf8865000b338c24c9fdd5e985183feaca10f))
* **api:** image generation actions for responses; ResponseFunctionCallArgumentsDoneEvent.name ([7d96513](https://github.com/openai/openai-python/commit/7d965135f93f41b0c3dbf3dc9f01796bd9645b6c))
* **client:** add custom JSON encoder for extended type support ([9f43c8b](https://github.com/openai/openai-python/commit/9f43c8b1a1641db2336cc6d0ec0c6dc470a89103))


### Bug Fixes

* **client:** undo change to web search Find action ([8f14eb0](https://github.com/openai/openai-python/commit/8f14eb0a74363fdfc648c5cd5c6d34a85b938d3c))
* **client:** update type for `find_in_page` action ([ec54dde](https://github.com/openai/openai-python/commit/ec54ddeb357e49edd81cc3fe53d549c297e59a07))

## 2.16.0 (2026-01-27)

Full Changelog: [v2.15.0...v2.16.0](https://github.com/openai/openai-python/compare/v2.15.0...v2.16.0)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "openai"
version = "2.16.0"
version = "2.17.0"
description = "The official Python library for the openai API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
7 changes: 5 additions & 2 deletions src/openai/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
APIConnectionError,
APIResponseValidationError,
)
from ._utils._json import openapi_dumps
from ._legacy_response import LegacyAPIResponse

log: logging.Logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -556,8 +557,10 @@ def _build_request(
kwargs["content"] = options.content
elif isinstance(json_data, bytes):
kwargs["content"] = json_data
else:
kwargs["json"] = json_data if is_given(json_data) else None
elif not files:
# Don't set content when JSON is sent as multipart/form-data,
# since httpx's content param overrides other body arguments
kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None
kwargs["files"] = files
else:
headers.pop("Content-Type", None)
Expand Down
6 changes: 3 additions & 3 deletions src/openai/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def model_dump(
exclude_defaults: bool = False,
warnings: bool = True,
mode: Literal["json", "python"] = "python",
by_alias: bool | None = None,
) -> dict[str, Any]:
if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
Expand All @@ -148,13 +149,12 @@ def model_dump(
exclude_defaults=exclude_defaults,
# warnings are not supported in Pydantic v1
warnings=True if PYDANTIC_V1 else warnings,
by_alias=by_alias,
)
return cast(
"dict[str, Any]",
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
exclude=exclude,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
),
)

Expand Down
35 changes: 35 additions & 0 deletions src/openai/_utils/_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from typing import Any
from datetime import datetime
from typing_extensions import override

import pydantic

from .._compat import model_dump


def openapi_dumps(obj: Any) -> bytes:
"""
Serialize an object to UTF-8 encoded JSON bytes.

Extends the standard json.dumps with support for additional types
commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
"""
return json.dumps(
obj,
cls=_CustomEncoder,
# Uses the same defaults as httpx's JSON serialization
ensure_ascii=False,
separators=(",", ":"),
allow_nan=False,
).encode()


class _CustomEncoder(json.JSONEncoder):
@override
def default(self, o: Any) -> Any:
if isinstance(o, datetime):
return o.isoformat()
if isinstance(o, pydantic.BaseModel):
return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
return super().default(o)
2 changes: 1 addition & 1 deletion src/openai/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "openai"
__version__ = "2.16.0" # x-release-please-version
__version__ = "2.17.0" # x-release-please-version
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class ResponseFunctionCallArgumentsDoneEvent(BaseModel):
item_id: str
"""The ID of the function call item."""

name: str
"""The name of the function that was called."""

output_index: int
"""The index of the output item in the response."""

Expand Down
17 changes: 12 additions & 5 deletions src/openai/types/responses/response_function_web_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
from ..._utils import PropertyInfo
from ..._models import BaseModel

__all__ = ["ResponseFunctionWebSearch", "Action", "ActionSearch", "ActionSearchSource", "ActionOpenPage", "ActionFind"]
__all__ = [
"ResponseFunctionWebSearch",
"Action",
"ActionSearch",
"ActionSearchSource",
"ActionOpenPage",
"ActionFind",
]


class ActionSearchSource(BaseModel):
Expand Down Expand Up @@ -41,17 +48,17 @@ class ActionOpenPage(BaseModel):
type: Literal["open_page"]
"""The action type."""

url: str
url: Optional[str] = None
"""The URL opened by the model."""


class ActionFind(BaseModel):
"""Action type "find": Searches for a pattern within a loaded page."""
"""Action type "find_in_page": Searches for a pattern within a loaded page."""

pattern: str
"""The pattern or text to search for within the page."""

type: Literal["find"]
type: Literal["find_in_page"]
"""The action type."""

url: str
Expand All @@ -74,7 +81,7 @@ class ResponseFunctionWebSearch(BaseModel):
action: Action
"""
An object describing the specific action taken in this web search call. Includes
details on how the model used the web (search, open_page, find).
details on how the model used the web (search, open_page, find_in_page).
"""

status: Literal["in_progress", "searching", "completed", "failed"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Union, Iterable
from typing import Union, Iterable, Optional
from typing_extensions import Literal, Required, TypeAlias, TypedDict

from ..._types import SequenceNotStr
Expand Down Expand Up @@ -49,17 +49,17 @@ class ActionOpenPage(TypedDict, total=False):
type: Required[Literal["open_page"]]
"""The action type."""

url: Required[str]
url: Optional[str]
"""The URL opened by the model."""


class ActionFind(TypedDict, total=False):
"""Action type "find": Searches for a pattern within a loaded page."""
"""Action type "find_in_page": Searches for a pattern within a loaded page."""

pattern: Required[str]
"""The pattern or text to search for within the page."""

type: Required[Literal["find"]]
type: Required[Literal["find_in_page"]]
"""The action type."""

url: Required[str]
Expand All @@ -82,7 +82,7 @@ class ResponseFunctionWebSearchParam(TypedDict, total=False):
action: Required[Action]
"""
An object describing the specific action taken in this web search call. Includes
details on how the model used the web (search, open_page, find).
details on how the model used the web (search, open_page, find_in_page).
"""

status: Required[Literal["in_progress", "searching", "completed", "failed"]]
Expand Down
3 changes: 3 additions & 0 deletions src/openai/types/responses/response_input_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ class ShellCallOutput(BaseModel):
output.
"""

status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
"""The status of the shell call output."""


class ApplyPatchCallOperationCreateFile(BaseModel):
"""Instruction for creating a new file via the apply_patch tool."""
Expand Down
3 changes: 3 additions & 0 deletions src/openai/types/responses/response_input_item_param.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ class ShellCallOutput(TypedDict, total=False):
output.
"""

status: Optional[Literal["in_progress", "completed", "incomplete"]]
"""The status of the shell call output."""


class ApplyPatchCallOperationCreateFile(TypedDict, total=False):
"""Instruction for creating a new file via the apply_patch tool."""
Expand Down
3 changes: 3 additions & 0 deletions src/openai/types/responses/response_input_param.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ class ShellCallOutput(TypedDict, total=False):
output.
"""

status: Optional[Literal["in_progress", "completed", "incomplete"]]
"""The status of the shell call output."""


class ApplyPatchCallOperationCreateFile(TypedDict, total=False):
"""Instruction for creating a new file via the apply_patch tool."""
Expand Down
5 changes: 4 additions & 1 deletion src/openai/types/responses/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ class ImageGeneration(BaseModel):
type: Literal["image_generation"]
"""The type of the image generation tool. Always `image_generation`."""

action: Optional[Literal["generate", "edit", "auto"]] = None
"""Whether to generate a new image or edit an existing image. Default: `auto`."""

background: Optional[Literal["transparent", "opaque", "auto"]] = None
"""Background type for the generated image.

Expand All @@ -247,7 +250,7 @@ class ImageGeneration(BaseModel):
Contains `image_url` (string, optional) and `file_id` (string, optional).
"""

model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"], None] = None
model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"], None] = None
"""The image generation model to use. Default: `gpt-image-1`."""

moderation: Optional[Literal["auto", "low"]] = None
Expand Down
5 changes: 4 additions & 1 deletion src/openai/types/responses/tool_param.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ class ImageGeneration(TypedDict, total=False):
type: Required[Literal["image_generation"]]
"""The type of the image generation tool. Always `image_generation`."""

action: Literal["generate", "edit", "auto"]
"""Whether to generate a new image or edit an existing image. Default: `auto`."""

background: Literal["transparent", "opaque", "auto"]
"""Background type for the generated image.

Expand All @@ -247,7 +250,7 @@ class ImageGeneration(TypedDict, total=False):
Contains `image_url` (string, optional) and `file_id` (string, optional).
"""

model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini"]]
model: Union[str, Literal["gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"]]
"""The image generation model to use. Default: `gpt-image-1`."""

moderation: Literal["auto", "low"]
Expand Down
Loading
Loading