Repository navigation
chore: sync current main into next before major development - #4126
jbeckwith-oai wants to merge 343 commits into
Conversation
Automated Release PR --- ## [3.7.0](v3.6.0...v3.7.0) (2026-09-02) ### Features * **api:** update usage APIs and documentation ([#3779](#3779)) ([6f0da16](6f0da16)) ### Bug Fixes * avoid repeated numpy checks for embeddings ([#3757](#3757)) ([b19c216](b19c216)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Summary - add docs/architecture/security-model.md as the canonical Codex Security threat model - keep SECURITY.md focused on coordinated disclosure and point it to the canonical model - clarify repository-code authority while preserving runtime parser, webhook, metadata, and CI/release boundaries ## Validation - verified 98 source citations resolve to existing files and valid lines - resolved SECURITY.md policy chain for docs/architecture/security-model.md - git diff --check - ./scripts/lint (blocked before linting by the preexisting base-state uv.lock needing update under --locked)
## Summary Add GPT-6 Astra support, Responses WebSocket steering, safety alert retrieval, safety webhooks, and expanded safety metadata. This changes the public ResponsesClientEvent models from response-creation classes into discriminated unions, requiring users who instantiate those classes to use ResponseCreate instead. ## Changes - Add `gpt-6-astra` to supported chat and Responses model literals. - Add synchronous and asynchronous safety alert retrieval through client.safety.alerts, including raw and streamed response access and the SafetyAlert type. - Add project and organization safety alert webhook event types and include them in webhook unwrapping. - Add response.steer WebSocket methods for stable and beta Responses clients, with text, image, and file input support. - Add steering client and server event types for accepted, pending, and failed states, including pending reasons, required input, and error codes. - Change ResponsesClientEvent and BetaResponsesClientEvent from response-creation model classes to discriminated unions covering response creation and steering; expose the original creation payloads as ResponseCreate and BetaResponseCreate while retaining aliases for existing helper types. - Extend response lifecycle, tool definition, and tool-call types with steering-related metadata and optional asynchronous tool execution support. - Add optional misalignment details and continuation guidance to shared, Responses WebSocket, and video error models. Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
Automated Release PR --- ## [3.8.0](v3.7.0...v3.8.0) (2026-09-03) ### Features * **api:** add gpt-6-astra and related features ([#3791](#3791)) ([09f446f](09f446f)) ### Documentation * add canonical SDK security model ([#3778](#3778)) ([2a097b9](2a097b9)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
migrate to forked steady
## Summary Web-search calls can represent the incomplete status in input and response types, including strict response validation. ## Changes - Accept incomplete in stable and beta web-search call status types. - Allow strict response validation of web-search calls with this status. Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
## Summary Expose optional prompt cache comparison diagnostics across the stable and beta Responses APIs. Clarify how Fast and Priority service tier requests resolve for different models. ## Changes - Add the optional `comparison_response_id` prompt cache option to response creation parameters and client events. - Add `prompt_cache_diagnostics` to response models with typed cache hit, cache miss, comparison-not-found, and unavailable outcomes. - Expose cache miss reasons, affected token estimates, and reusable-prefix token counts in diagnostic results. - Include the comparison response ID in returned prompt cache options when supplied. - Clarify that Fast or Priority requests resolve to `fast` for models with a dedicated Fast tier and to `priority` for other models. Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
Positive numeric Retry-After values that overflow a float previously selected short fallback backoff. Treat them as excessive server delays so synchronous and asynchronous requests return the original API error without retrying early. Preserve the existing 120-second limit, header precedence, and malformed/literal-infinity handling. Validation: 376 client and X.509 hardening tests passed (2 existing skips), including 24 public-entrypoint overflow/precedence cases; Ruff, Mypy, and Pyright passed. The new module failed 10 cases before the fix. Two independent adversarial review rounds are clean. Exact-commit custom-code budget: 6,626 / 10,000 lines.
…-545) (#3801) ## Summary Removes the name model field from response.function_call_arguments.done to match the API response. ## Changes - Remove the name model field from stable and beta Responses function argument completion events. - Update code that accesses the removed field to read name from the function-call item matched by item_id. Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
#3803) ## Summary Document client throttling and model overload responses for moderation and video requests. ## Changes - Clarify the 429 slow_down and 503 server_is_overloaded response contracts - Document the optional minimum wait before retrying Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
Automated Release PR --- ## [3.9.0](v3.8.0...v3.9.0) (2026-09-05) ### Features * **api:** Add prompt cache diagnostics ([#3800](#3800)) ([8326784](8326784)) * **api:** correct function argument completion event fields (openapi-545) ([#3801](#3801)) ([2a98f6a](2a98f6a)) ### Bug Fixes * **api:** accept incomplete web search call statuses ([#3786](#3786)) ([3cc8d78](3cc8d78)) * refuse overflowing server retry delays ([#3799](#3799)) ([88b4d43](88b4d43)) ### Chores * **api:** document throttling and model overload responses — SDK-235 ([#3803](#3803)) ([be92815](be92815)) * migrate to forked steady ([#3795](#3795)) ([b24aeda](b24aeda)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Summary Adds optional expiration fields for service-account API keys, clarifies the Responses store option, and honors WebSocket retry limits across repeated reconnects. ## Changes - Allow service-account API key creation requests to specify expires_in_seconds. - Expose expires_at on service-account API key responses. - Clarify the default and retention behavior of the Responses store option. - Honor the configured retry limit when WebSocket connections repeatedly fail before receiving a non-error application event. --------- Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com> Co-authored-by: Marcus Wood <marcuswood@openai.com>
Adds GPT Image 2.5 Sunburst/Flare aliases and September 8 snapshots, `xhigh`/`max` quality, and custom image dimensions. - Exposes optional, nullable `quality` and `size` on Responses image calls, including `quality: "auto"`. - Clarifies Live and Realtime webhook call IDs. - Preserves WebSocket reconnect retry accounting when raw events are received. Co-authored-by: marcuswood-oai <323319819+marcuswood-oai@users.noreply.github.com>
Automated Release PR --- ## [3.10.0](v3.9.0...v3.10.0) (2026-09-08) ### Features * **api:** add GPT Image 2.5 models and image options ([#3824](#3824)) ([5b39c45](5b39c45)) * **api:** add service-account API key expiration fields ([#3802](#3802)) ([f1cd7f0](f1cd7f0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Summary Allow project service accounts to create initial API keys with a configurable lifetime and expose their expiration timestamps. Clarify related API behavior and refresh inline documentation links. ## Changes - Add the optional `expires_in_seconds` parameter to synchronous and asynchronous service account creation and its request type. - Document that key lifetimes may be required or limited by policy and cannot be set with `create_service_account_only`. - Add `expires_at` expiration metadata to project API key and service account creation response types. - Clarify that transcription prompts are unsupported with `gpt-4o-transcribe-diarize`. - Clarify that incoming call webhook SIP headers exclude authorization headers, preserve repetitions and ordering, and must be treated as untrusted metadata. - Point SDK resource and type documentation to the current API guides and references. Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
Automated Release PR --- ## [3.11.0](v3.10.0...v3.11.0) (2026-09-09) ### Features * **api:** Add expiration controls for service account keys ([#3825](#3825)) ([f348ec8](f348ec8)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Problem and root cause Test jobs jumped on August 27 at #3641 (`f3f96317e`), which added the dependency-security checker and its policy regression matrix. Comparing adjacent main runs: | Python 3.14 | Before #3641 | After #3641 | This PR | | --- | ---: | ---: | ---: | | Entire test job | 2m24s | 7m00s | **2m41s** | | Pydantic v2 pytest | 60.48s | 192.88s | **62.75s** | | Pydantic v1 pytest | 46.60s | 183.24s | **52.60s** | | Passing tests per v2 run | 9,144 | 11,169 | 11,306 | [Before run](https://github.com/openai/openai-python/actions/runs/33028653786) · [After run](https://github.com/openai/openai-python/actions/runs/33090881664) · [This PR](https://github.com/openai/openai-python/actions/runs/34386187641) The suites account for 269 of the 276 additional seconds. Large-payload tests were already present while jobs remained around 2m10s; the Steady migration happened afterward. Setup is not the primary cause. The current security-floor matrix has 1,760 cases. Each previously launched a Python checker and up to four Python Git stubs. This process and compilation overhead repeats under both Pydantic versions in all three test jobs. A representative local profile spent 85ms of 94ms in subprocess calls, with little time in the policy algorithm. A 20-iteration harness comparison measured median 180ms per subprocess case versus 0.9ms in-process. ## Change Compile the same security checker once per pytest worker and execute each policy case with fresh globals, its existing file fixtures, and the same Git stub. Restore patched process state after each execution. Preserve real subprocess execution for CLI parity cases and the existing trusted-base, isolation, and workflow integration tests. All existing policy cases, SDK coverage, Pydantic lanes, Python versions, large-payload sizes, and production enforcement remain unchanged. Add slow-test reporting to pytest configuration so future regressions are visible.
## Summary Fork PRs #3760 and #3345 have green Python CI but no required Castiron budget statuses: both the workflow-run association and commit-to-PR endpoint return no PRs. The trusted evaluator fails with `source run must identify exactly one current PR targeting main`, and the publisher returns without posting statuses. Fall back to listing open PRs by the source run's fork owner and branch, then retain the existing exact-head, target, freshness, and ambiguity checks. Apply the fallback to report computation, budget evaluation, and publication; update the reporter integrity digest. ## Validation - Regression tests reproduce the evaluator failure and missing statuses on unpatched main. - Patched read-only lookup resolves the exact current heads of both affected PRs. - Castiron reporter/budget suite: 53 tests, OK (1 optional compiler-contract test skipped), including the JavaScript publishers, pagination, stale heads, wrong targets, and ambiguous matches. - Ruff, Pyright, and mypy for `scripts/castiron`. The trusted handler runs from main. After this fix merges, rerun the Castiron custom-code workflows for #3760 and #3345 to publish their required statuses.
…#3760) Fixes #3338 Fixes #3341 ## The bug `transform()` and `construct_type()` both assume that any `dict` or `list` annotation is parameterised, and index into `get_args()` unconditionally. For a bare, unparameterised annotation `get_args()` returns an empty tuple, so the index access raises instead of transforming/constructing the value. ```py from typing import TypedDict from openai._utils._transform import transform from openai._models import construct_type class Params(TypedDict, total=False): metadata: dict # bare dict — no type parameters transform({"metadata": {"key": "value"}}, Params) # IndexError: tuple index out of range (_transform.py:183) construct_type(value={"key": "value"}, type_=dict) # ValueError: not enough values to unpack (expected 2, got 0) (_models.py:660) ``` Two details that weren't in the linked issues: - **Bare `list` is affected too.** `construct_type(value=[1, 2], type_=list)` raises `IndexError` at `args[0]`, and `transform([1, 2], list)` raises `RuntimeError: Expected type <class 'list'> to have a type argument at index 0` via `extract_type_arg`. Same root cause, so it's fixed here as well. - **This reaches real response deserialization.** Any `BaseModel` with a bare `dict`/`list` field crashes on construction, because `Model.construct()` routes through the same `construct_type()` branch: ```py class M(BaseModel): metadata: dict M.construct(metadata={"a": 1}) # ValueError: not enough values to unpack (expected 2, got 0) ``` The async transform path (`_async_transform_recursive`) has the same two call sites and is fixed alongside the sync one. ## The fix Bare containers are treated as if their contents were annotated with `Any`, which is exactly the existing behaviour for `dict[str, Any]` / `list[Any]` — values pass through unchanged, while nested `BaseModel` values still get dumped for JSON-serializability. This keeps parameterised and unparameterised annotations consistent rather than special-casing bare ones into a "return unchanged" path. - `_transform.py`: new `_extract_container_arg(typ, index)` helper that falls back to `object` when the index is out of range, used by the `dict` and `list`/`Iterable`/`Sequence` branches in both the sync and async recursion. - `_models.py`: `args` is already computed at the top of `construct_type`, so the `dict` and `list` branches just guard the index access. ## Verification Added `test_bare_dict_annotation` / `test_bare_list_annotation` to `tests/test_transform.py` (parametrised over sync and async) and `tests/test_models.py`. All six new cases fail on `main` and pass with this change. `ruff check`, `ruff format --check`, `pyright`, and `mypy` are clean on the four touched files. `tests/test_models.py`, `tests/test_transform.py` and `tests/test_utils` pass, with no change to the set of pre-existing failures. --------- Co-authored-by: Marcus Wood <marcuswood@openai.com>
## Summary Responses parsing raised `TypeError` when a response contained `output: null`. Treat null output as empty for standalone parsing, and recover finalized `response.output_item.done` items when a streaming completion has null or missing output. Recovery preserves item order, final statuses, annotations, refusals, structured output, tool arguments, and completion metadata. Explicit completion output, including `[]`, remains authoritative. This addresses the review feedback about losing streamed output without adding per-item finalization branches. Fixes #3325. ## Validation - Merged current `main` into the contributor branch. - Responses suite: 48 passed with Pydantic v2 and 48 passed with Pydantic v1 on Python 3.10. - Public synchronous and asynchronous streaming coverage includes null, missing, empty, and supplied completion output, with and without streamed items. - Ruff, Pyright (0 errors), Mypy (1,614 source files), and import check passed. - Full PR diff reviewed; no unrelated changes or security findings. --------- Co-authored-by: Marcus Wood <marcuswood@openai.com>
## Summary Add `AsyncStream.aclose()` as a thin async alias for the existing `close()` method. This supports `contextlib.aclosing()` and adapters that expect `aclose()` while preserving existing `close()` and async context manager behavior, including subclass overrides of `close()`. Fixes #2853. The reported cleanup error occurs when an instrumentation wrapper exposes an `AsyncStream` as its response and the higher-level chat streaming helper calls `aclose()` on it. The alias makes that cleanup path work without changing stream iteration or response handling. ## Tests - Verify that `aclose()` closes an actual streaming HTTP response and remains safe alongside repeated `close()` calls. - Exercise `contextlib.aclosing()` through the public async client after an early exit and an exception. - Merge current main and adapt the original test to HTTPX2. ## Validation - 23 streaming tests pass on Python 3.10 with both Pydantic v1 and v2. - 16 additional local compatibility checks pass with both Pydantic versions, covering existing cleanup methods, unread/partial/complete streams, subclass overrides, and the reported instrumentation cleanup path. - Broad local run: 5,108 passed, 120 skipped; three environment-related failures passed on rerun after installing local wheel metadata and using CI-pinned uv 0.12.5 (5,111 passing tests total). - Ruff, Pyright, Mypy, import checks, and changed-file formatting pass. - Wheel and source distribution builds, Python metadata validation, Bedrock wheel smoke checks, and isolated HTTPX2/legacy HTTPX/aiohttp wheel checks pass. Local limitation: the pinned Steady mock server could not download its JSR dependencies in this environment. Forty fine-tuning tests failed because the server was unavailable, and API-resource tests were not run locally. CI still needs to validate the full mock-server suite and supported Python matrix. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Marcus Wood <marcuswood@openai.com>
- Add Live API endpoints. - Add synchronous and asynchronous WebSocket helpers for primary, sideband, and fork connections. - Add `TranscriptGrouper` and `AsyncTranscriptGrouper` helpers. Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
Automated Release PR --- ## [3.12.0](v3.11.0...v3.12.0) (2026-09-10) ### Features * **api:** Add Live API ([0e4bfef](0e4bfef)) ### Bug Fixes * add aclose() to AsyncStream for standard async cleanup ([#2854](#2854)) ([802b334](802b334)) * handle bare `dict` and `list` annotations without type arguments ([#3760](#3760)) ([c7e8c03](c7e8c03)) * preserve finalized output on null response completion ([#3345](#3345)) ([adb212e](adb212e)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Add the `beta.agents` API. - Manage agents, sessions, items, and turns; inspect subagents and their history. - Work with environments, templates, files, artifacts, vaults, and credentials. - Includes automatic pagination, streaming events, binary artifact downloads, and the `OpenAI-Beta: agents=v1` header. - `beta.agents.sessions.stream(...)`: Stream one turn on an idle session; supports sync and async clients. - Function-tool handlers: Parse arguments, invoke registered functions, and submit tool results during the stream. - `AgentSessionMessage.output_text`: Join a message’s output-text blocks into one string. Co-authored-by: apcha-oai <228803254+apcha-oai@users.noreply.github.com>
Automated Release PR --- ## [3.13.0](v3.12.0...v3.13.0) (2026-09-10) ### Features * **api:** add Agents API ([1c4284a](1c4284a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Fixes #3201. ## Summary Streaming tool call deltas can include multiple entries with the same logical `index` in the first chunk. The accumulator used to assign that first list wholesale when the key was absent or `None`, so duplicate-index entries bypassed the existing recursive merge path and later argument fragments merged into the wrong physical list entry. This changes the streaming delta accumulators to initialize indexed lists through the existing merge loop instead of taking the first indexed list as-is. It also applies the same initial-list normalization to the chat completion snapshot path and the matching assistant streaming accumulator, which shared the same early-return behavior. ## Root cause `accumulate_delta()` already knew how to merge list entries by their logical `index`, but only after an accumulator entry existed. When the first streamed value for `tool_calls` contained two entries with `index: 0`, the fast path copied both entries directly into the snapshot. The next chunk then merged into physical slot `0` while the earlier arguments fragment stayed orphaned in physical slot `1`, producing invalid final JSON. ## Validation Reproduced on current `origin/main` with a minimal script: duplicate `index: 0` entries remained as two physical list entries and the assertion that they merge failed. ```text env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY -u http_proxy -u https_proxy -u all_proxy uv run --with pytest --with pytest-xdist --with pytest-asyncio --with respx --with inline-snapshot --with syrupy --with anyio --with pydantic --with httpx --with distro pytest tests/lib/test_streaming_deltas.py tests/lib/chat/test_completions_streaming.py::test_duplicate_tool_call_index_in_initial_chunk -q # 3 passed uv run --with ruff ruff check src/openai/lib/streaming/_deltas.py src/openai/lib/streaming/_assistants.py src/openai/lib/streaming/chat/_completions.py tests/lib/test_streaming_deltas.py tests/lib/chat/test_completions_streaming.py # All checks passed! uv run --with ruff ruff format --check src/openai/lib/streaming/_deltas.py src/openai/lib/streaming/_assistants.py src/openai/lib/streaming/chat/_completions.py tests/lib/test_streaming_deltas.py tests/lib/chat/test_completions_streaming.py # 5 files already formatted git diff --check origin/main..HEAD # passed ``` --------- Co-authored-by: Marcus Wood <marcuswood@openai.com>
## Summary The README vision example points to a Wikimedia thumbnail URL that can fail during the API's image download. Replace it with the National Gallery of Art image already used in the [OpenAI vision guide](https://developers.openai.com/api/docs/guides/images-vision), preserving the current Responses API example and model. Fixes #2776. ## Image provenance The image UUID `a2e6da57-3cd1-4235-b20e-95dcaefed6c8` is listed with `openaccess=1` in the National Gallery's [official published-image catalog](https://github.com/NationalGalleryOfArt/opendata/blob/main/data/published_images.csv). The Gallery's [open-access policy](https://www.nga.gov/terms-and-notices) releases these images under CC0 for commercial and non-commercial use. The URL is hosted directly by the Gallery and matches the official OpenAI docs example. ## Validation - Ran the exact updated README URL example against the live Responses API with SDK 3.13.0 and `gpt-5.5`: status `completed`, with a description of the seated woman in the portrait. - `git diff --check` passed. - Reviewed the full diff: one image URL replacement in README.md.
Fixes #3125. Skip null or missing `response.output_item.added` items and track valid items by their original stream indexes. Later text and tool-call deltas reach the correct item even when indexes have gaps. Content for an item never received raises a clear `RuntimeError` instead of being silently discarded. Preserves the finalized-output recovery from #3345 and keeps generated event types unchanged. Merged current main into the contributor branch and resolved the test conflicts. ## Validation - Responses suite: 95 passed with Pydantic v2 and 95 passed with v1 on Python 3.10. - Broader handwritten SDK and streaming suite: 2,125 passed, 83 skipped. - Sync/async mock-stream coverage includes missing/null items, reused and sparse indexes, text/tool deltas, empty/null/missing initial output, and completion recovery. The original guard fails the new gap regression. - Ruff, Pyright (0 errors), Mypy (1,834 source files), compilation, and import smoke checks passed. - Forty fine-tuning tests require the local Steady server. Its dependency download from JSR failed; a representative connection failure also reproduces with main. These tests were excluded from the passing broad run. Reviewed the complete diff; no unrelated changes or security findings. --------- Co-authored-by: nightcityblade <nightcityblade@gmail.com> Co-authored-by: Marcus Wood <marcuswood@openai.com>
Fixes #3097 ## Summary - add an optional `max_wait_seconds` bound to `vector_stores.files.poll()` - thread the same bound through sync/async `create_and_poll()` and `upload_and_poll()` - raise `TimeoutError` instead of waiting forever when a vector store file stays `in_progress` ## Why `upload_and_poll()` currently waits until the vector store file reaches a terminal state. If the backend leaves the vector store file in `in_progress` indefinitely, the helper never returns and the caller has no SDK-level escape hatch. The new argument keeps the default behavior unchanged, while letting callers opt into a bounded wait. ## Validation - `PYTHONPATH=src python -m pytest tests/api_resources/vector_stores/test_files.py::TestFiles::test_poll_timeout tests/api_resources/vector_stores/test_files.py::TestAsyncFiles::test_poll_timeout -q` - `PYTHONPATH=src python -m pytest tests/api_resources/vector_stores/test_files.py::test_create_and_poll_method_in_sync tests/api_resources/vector_stores/test_files.py::test_upload_and_poll_method_in_sync -q` - `PYTHONPATH=src python -m ruff check src/openai/resources/vector_stores/files.py tests/api_resources/vector_stores/test_files.py` - `python -m compileall -q src/openai/resources/vector_stores/files.py tests/api_resources/vector_stores/test_files.py` - `git diff --check` - [x] I understand that this repository is auto-generated and my pull request may not be merged Co-authored-by: Marcus Wood <marcuswood@openai.com>
## Summary A timeout or broken connection after streaming starts currently escapes as a raw HTTPX exception, bypassing `except openai.APIError`. Wrap request failures at the event-read boundary, preserving the original exception as `__cause__` and closing the response. Keep parsing and callback errors unchanged. Preserve existing Assistants event-handler and raw byte-stream behavior. Partially consumed streams are not retried. Fixes #3811. Supersedes the overlapping fixes in #3813, #3814, and #3818. ## Release note Release as a **minor version**. `Stream` and `AsyncStream` now raise `openai.APITimeoutError` for read timeouts and `openai.APIConnectionError` for other HTTPX request failures, including decoding errors. Applications catching raw `httpx` or `httpx2` exceptions during event streaming should catch these SDK exceptions instead. The underlying exception remains available through `__cause__`. ## Validation - Merged current main and resolved the test-import conflict. - Six after-first-chunk regression cases fail on main and pass with this change. - 77 focused tests pass on each of Pydantic v1 and v2, including sync/async HTTPX2 and legacy HTTPX, response cleanup, no retries, and callback compatibility. - Broad offline suite: 3,658 passed, 132 skipped; large-payload contract: 1 passed, run sequentially. - Ruff, Mypy, and focused Pyright pass. - Custom-code budget passes: 6,827 / 10,000 lines. - Full diff security review found no unrelated changes or dependency, credential, or workflow modifications. API/mock-server and network-dependent suites were excluded from the broad local run. The separate optional legacy aiohttp adapter test could not run because `httpx_aiohttp` is not installed. --------- Co-authored-by: Marcus Wood <marcuswood@openai.com>
Automated Release PR --- ## [3.33.0](v3.32.0...v3.33.0) (2026-10-16) ### Features * **api:** add safety identifiers to image requests and responses ([#4093](#4093)) ([c969e32](c969e32)) * **api:** share Decisions cache breakpoint parameter type ([#4092](#4092)) ([f7cefda](f7cefda)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Why Python callers need to send audio for Translation and receive its final audio and transcript events before closing. ## High-level description Add `client.realtime.translations.connect(model=...)` and sync/async session helpers. Direct connections include the model on every connection attempt and accept large audio events by default; caller-set receive limits still apply. The session helpers send the finish command once, reject later input, keep trailing events visible, and close the connection on exit. They never reconnect or replay commands. Existing API models and released public signatures stay unchanged. The Live byte receiver also keeps its existing contract on websockets 13. ## Validation - The prior full local run passed 1,399 Translation, Realtime, Responses, Live, redirect, and recovery tests. - After the review fixes, all 41 Translation tests pass with current dependencies and with Pydantic 1/websockets 13.1. They cover direct/session 5 MiB events, explicit receive limits, and model retention during reconnects. - The rebuilt installed wheel completes four direct/session sync/async connections with fresh credentials, request hooks, events, finish, and cleanup. - Repeat generation matches all 1,963 files. The custom-code budget passes at 7,364/10,000 lines. Mypy, Pyright, and scoped Ruff checks pass. - [CI passes on the updated commit](https://github.com/openai/openai-python/actions/runs/39015378913), as do CodeQL and custom-code checks. Hosted service tests remain on hold. Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
## Summary Recognize degraded as an external storage status. ## Changes - Add degraded alongside pending, validated, and unhealthy while retaining existing status values.
Automated Release PR --- ## [3.34.0](v3.33.0...v3.34.0) (2026-10-17) ### Features * **api:** add degraded external storage status ([#4097](#4097)) ([b216cb2](b216cb2)) * **api:** add Realtime Translation WebSocket sessions ([#4095](#4095)) ([bfada50](bfada50)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Why Synchronous Realtime reads pass a `decode` argument that websockets 13 does not support, despite that version being in the supported range. ## High-level description Use the existing byte-receive compatibility helper. Add a real-socket regression for Unicode text, binary frames, and the version-13 receive signature. Public signatures, dependencies, and reconnect behavior stay unchanged. ## Validation 29 focused Realtime contract/recovery tests pass with websockets 13.1 and with 15.0.1. Ruff and diff checks pass. Hosted tests remain on hold. Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
## Summary Add typed access to reflected input/output audio and Live transport events. ## Changes - Recognize reflected input/output audio, DTMF, ringing, answered, and failed events on Live sideband connections. ## Why Live sideband connections lacked typed access to reflected input/output audio and transport events. ## High-level description Add the seven existing sideband event variants while retaining existing public event classes and unknown-event handling. Connection runtimes and API helpers remain SDK-owned. Existing HTTP APIs, credential policy, and recovery behavior stay unchanged. ## Validation The new sync and async sideband tests pass from source and from an installed wheel. They cover all seven added event variants, null and omitted client event IDs, unknown events, and cleanup. Hosted-service tests remain on hold. [Current CI](https://github.com/openai/openai-python/pull/4099/checks). Co-authored-by: markstuart-oai <323302876+markstuart-oai@users.noreply.github.com>
## Summary Allow ultrafast service selection in Agents model settings. ## Changes - Include ultrafast in typed service-tier options for agent creation, updates, and session model settings. Scope review: the transformed API changes only the ultrafast enum value and its matching description from OpenAPI 8ba0678. Existing service-tier values and public method types remain available.
Automated Release PR --- ## [3.35.0](v3.34.0...v3.35.0) (2026-10-17) ### Features * **api:** add typed Live sideband events ([#4099](#4099)) ([8d5cbf0](8d5cbf0)) * **api:** add ultrafast service tier for Agents ([#4101](#4101)) ([100bf8f](100bf8f)) ### Bug Fixes * **realtime:** preserve byte receives with websockets 13 ([#4098](#4098)) ([309916e](309916e)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Why Synchronous beta Realtime calls `recv(decode=False)`, but the supported websockets 13.x sync API has no `decode` argument. Receiving a message therefore raises TypeError. ## High-level description Use the existing SDK byte-receive helper, as the other synchronous APIs already do. Extend the real-socket text/binary regression to beta Realtime, including the legacy dependency signature. Add wire-level tests across all eight connection entrypoints for sync/async redirects, fresh credentials, explicit header precedence, events, and cleanup. Preserve current redirect and connection behavior. ## Validation All 191 tests in the two focused modules pass with both websockets 13.1 and 15.0.1. The unpatched published SDK 3.35.0 reproduces the beta receive failure on 13.1 (two failing wire cases); with 15.0.1 it passes all 64 new wire cases and 263 existing functional cases. Ruff checks pass. All servers are local and credentials synthetic; no hosted tests. ## Release note Fix synchronous beta Realtime message reception when using websockets 13.x. Public signatures and returned bytes remain unchanged. A locally built wheel containing this fix also passes all 64 new connection cases with websockets 13.1 when installed outside the source tree. Focused Pyright checks pass. [Current CI checks](https://github.com/openai/openai-python/pull/4102/checks)
Automated Release PR --- ## [3.35.1](v3.35.0...v3.35.1) (2026-10-18) ### Bug Fixes * preserve beta Realtime byte reads on websockets 13 ([#4102](#4102)) ([5766349](5766349)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
## Summary Use a generic placeholder for text in moderation examples. ## Changes - Replace the explicit text example with a neutral placeholder. - Keep moderation parameters, response types, and behavior unchanged. <!-- moderation-follow-up --> The moderation request uses a neutral placeholder. Its obsolete response example is removed because it described the previous input; response examples are optional. API parameters, response types, and endpoint behavior are unchanged.
## Summary Accept valid vector-store file-batch responses when strict response validation is enabled. ## Changes - Preserve the returned object discriminator and validation of other response fields. Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
## Summary Expose 30-minute cache-write token counts in completions and Decisions usage results. ## Changes - Add the optional input_cache_write_30m_tokens usage metric. - Allow the metric to be null or absent in older responses. ## Validation - 16 synchronous/asynchronous transport cases pass; custom-code budget passes at 7,368 / 10,000 lines. - Handwritten tests cover 73, zero, explicit null, and omission, and preserve the existing cache-write counters. Isolated decoder mutations fail on the expected value; the unchanged candidates pass again. Co-authored-by: jbeckwith-oai <269036200+jbeckwith-oai@users.noreply.github.com>
Restore the two Castiron reporting files to the verified generated snapshot (`12578924cd37047e1bff02ea0e6f90114b363d15`). This removes a local variable rename and restores the generated reporter assertions, including coverage that workflow/action ownership is visible when explicitly requested. It eliminates 2 mixed files and 17 custom patch lines without changing the budget, ownership rules, provenance, or SDK API. Validation: - `python3 -m unittest discover -s scripts/castiron -p 'test_custom_code*.py'`: 95 tests run: 94 passed and one compiler-binary contract test skipped because `CASTIRON_TEST_BIN` is unset. - Both files match the verified generated snapshot byte for byte. - The budget evaluator AST is identical after normalizing the local variable name. - A mutation that drops `include_workflows=True` fails the restored ownership assertion. - Local budget checker: budget and isolation pass, with 39 mixed files and 7,351 custom patch lines against the verified snapshot (down from 41 files and 7,368 lines); the existing ceiling is 10,000.
The Python SDK has 41 generated-owned files carrying 7,368 custom patch lines (6,081 added and 1,287 deleted) at the pinned audit revision. Add a complete per-file inventory, current/generated physical line counts, and a compatibility-preserving plan to eliminate those patches through compiler facts and handwritten runtime boundaries. The plan partitions all files into 17 tracks, records exact contracts and test requirements, distinguishes moved signatures and overload churn from runtime logic, and defines compiler-before-SDK dependencies. No developer-facing breaking changes are permitted. Counting, budget, generation provenance, CODEOWNERS, and immutable fixture policies stay intact. Existing compiler audio/compatibility features are identified for reuse; their presence in the verified snapshot means remaining patches require exact compatibility analysis. Independent execution is underway: - #4113 restores two reporting files to verified generated output: measured 41 → 39 mixed files and 7,368 → 7,351 patch lines. Local offline suite: 94 passed, one compiler-binary test skipped; byte/AST equivalence and budget/isolation checks pass. - #4112 adds 16 synchronous/asynchronous public-entrypoint upload tests. Tests, Ruff, targeted mypy and pinned Pyright pass after repairing the initial CI typing findings. Reversed completion ordering caused the intended failures. - Separate upstream changes are submitted for Live queue generation, the exact file-processing delegate, and two authored-schema wording fixes. Compiler/runtime tests and raw parent-output comparisons pass. SDK promotion waits for required compiler merges and verified regeneration; projected reductions are not claimed as merged. Validation: the existing reporter verified snapshot metadata, ancestry and content hash and reproduced all 41-file counts. Every per-file current/generated physical size, patch identity, JSON/Markdown total and local link was checked; independent review confirmed the inventory and resource contracts. Compiler validation details and local environment limitations are recorded in the plan and upstream reviews. This PR changes documentation only.
Add handwritten compatibility tests for `videos.poll()` and `videos.create_and_poll()` before their custom generated-file implementations move behind compiler-supported boundaries. The existing generated suite covers individual endpoints and helper signature synchronization; this suite exercises the public synchronous and asynchronous clients with in-memory HTTP transports. Cover terminal and future statuses, queued/in-progress polling, default/server/explicit/zero intervals, helper headers, multipart creation fields, per-call option scoping, error propagation, and asynchronous cancellation. All payloads and uploads are synthetic. Validation: - 21 focused tests passed. - Focused Ruff lint and formatting checks passed. - Strict Pyright 1.1.413 reported no errors or warnings. The repository pins 1.1.414, which was unavailable locally; the pinned check remains for CI. - Negative control: offsetting the explicit polling header by one in both runtime methods caused six expected failures across synchronous and asynchronous polling/creation cases. Restoring the exact runtime bytes returned all 21 tests to green. One existing coverage gap needs separate runtime work: the advertised dictionary-shaped `ImageInputReferenceParam` currently fails file extraction in `create()`. This suite uses the supported upload tuple and adds no rejection contract for dictionaries.
Groups future transition work into coherent capability/resource families, with focused tests, typing fixtures, API-reference changes and ledger updates included. The guide is roughly 300–800 authored lines when natural; larger exact generated diffs remain reviewable through per-file counts and compatibility evidence. The next polling family covers Files, Videos and vector-store files/batches; compatible upload, export, WebSocket and parsing work follows the same policy. Existing submitted work keeps its review progress. Compiler-before-SDK-promotion, zero developer-facing breaking changes, security/CODEOWNER gates and separate ratchet-only budget changes remain mandatory. Routine status updates are batched with implementation. Records the first merged reduction in the Python generated-code transition: #4113 merged as `9d12b565554c08bcee6287c0d8f671b8c97ed911`, and its post-merge push checks are green. Verified remaining customization is 39 files / 7,351 patch lines, down from 41 / 7,368. The original per-file audit stays frozen; the JSON inventory adds a separate merged-progress record with current totals and restored paths. Plan #4114 has also actually merged as `6a52987f2da5c3bc9b700a7094c0cf14fe7e78dc`; Conductor separately verified post-merge push CI, CodeQL and release checks successful on that merge commit, including https://github.com/openai/openai-python/actions/runs/39316813847 . This branch is reconciled onto that main commit, and the complete PR diff contains only the two documentation files. The ledger records the two independent successors started automatically after the confirmed merge: handwritten video polling compatibility tests in #4115 and the now-submitted compiler generation of existing Beta resource aliases (72 configuration and 680 renderer tests, runtime/actual-SDK contracts and raw parent parity passed; all normal publication hooks enabled). Its one-file/21-line reduction remains projected until compiler merge and verified SDK generation. SDK CODEOWNER/security approval, compiler-before-promotion, normal protected merge gates and zero developer-facing breaking changes remain mandatory. Review requests/updates use deduplicated root #sdk-reviews posts. Validation: exact trusted-main reporter source verifies the same snapshot ancestry/hash, two restored files and all 39 remaining customizations unchanged. Local Git batch I/O needed a temporary-file stdin adapter; the source, hash/counting logic and returned Git bytes were unchanged, as documented. Frozen JSON identity, totals/physical-size reconciliation, relative documentation links and `git diff --check` pass. Independent review found no blockers. This changes only two documentation files, with no runtime, API, budget-policy, counting, provenance, dependency or fixture changes. Ready for normal current-head CI and required reviews. No merge, queue or post-merge success is inferred from earlier-head results. Owning Codex conversation: 6ad66292-b06c-8191-b9f2-baafa5fb801d. Conductor should route transition feedback and confirmed merge events to this chat so the next eligible batch proceeds automatically.
When a caller closes a recovering WebSocket while the replacement handshake is still pending, the new connection can take over after close. Canceling async recovery can also leave the connection accepting commands into a queue that will never drain. Use the existing sync close/publication lock for every manager and retire the queue as soon as close starts. Async recovery checks close before and after the handshake, closes an unwanted replacement even when its caller cancels, and clears the recovering flag on every exit. This preserves the existing rule against replaying a failed queue flush. Validation: 30 targeted tests pass across Realtime, transcription, sideband, Responses and Live; the initial regression tests failed on the previous source. Existing WebSocket suites passed 1,108 tests with 2 live-test skips (one new test exceeded its original 2-second barrier under parallel load; it now uses a bounded 10-second barrier and passes). Full Ruff, Mypy, Pyright, import, and the trusted custom-code budget check pass. The local `scripts/lint` wrapper could not resolve this environment's uv registry lock. Its underlying checks passed in a frozen all-extras environment; Pyright used the repository's exact Node, pnpm and Pyright pins.
Chunked upload helpers currently live alongside generated resource code. Add public-entrypoint compatibility tests before moving their implementation behind compiler-generated delegates, preserving the behavior callers already receive. The tests cover synchronous and asynchronous clients with in-memory HTTP transports: path and bytes inputs, empty payloads, inferred and explicit metadata, ordered parts and completion IDs, MD5 forwarding and omission, client default headers/query options, required metadata validation before requests, and file handle cleanup after successful or failed part uploads. All requests and payloads are synthetic. This change adds one handwritten test file and makes no SDK, dependency, generated-file, or budget changes. Validation: - `PYTHONPATH=src python -m pytest tests/lib/test_uploads.py -n 0`: 16 passed using the committed dependency lock on Python 3.10. - Ruff check and format check: passed. - Targeted mypy with `--follow-imports=silent`: passed. - Targeted Pyright 1.1.414: 0 errors and 0 warnings. The follow-up commit uses an explicit async-generator return annotation and a typed async file recorder to satisfy strict CI type checking. - Mutation check: reversing completion part IDs caused the six ordered multipart cases to fail; the SDK source was restored afterward. The explicit chunk-size cases cover multiple parts and a shorter final part. The default 64 MiB boundary is outside this test slice. Request SDK CODEOWNER review before merging.
The synchronous WebSocket client never supports `write_limit`; its `max_queue` option requires websockets 14 or newer. The SDK currently describes both sync and async connections with one options type. Add `SyncWebSocketConnectionOptions` to the SDK-owned options module with the five sync fields and the version note. Keep the existing `WebSocketConnectionOptions` and legacy `WebsocketConnectionOptions` alias fully compatible for async callers. This prepares the owned type for a separate generated-code update: current generated sync method annotations and the top-level export are unchanged in this PR. Validation: the new type contract failed before the change and passes afterward. Full Pyright, Mypy (2,036 files), Ruff and formatting passed. After rebasing onto the latest recovery fix, 132 focused options, recovery, Live and Translation checks pass; targeted Pyright remains clean. The trusted budget check passes at the current PR head.
With Pydantic v1, a valid Responses function call containing both `"async": true` and an extra `"async_": "future"` can replace the validated boolean with that extra value. The same problem affects nested validated SDK models with other aliased fields. Preserve the declared wire field during Pydantic v1 validation and discard only the colliding extra. Keep unrelated extra fields, input dictionaries, and keys used as another declared wire alias intact. Pydantic v2 and loose construction keep their existing behavior. Tests cover public synchronous and asynchronous Responses parsing, valid and future union variants, generic models, and overlapping declared aliases. On Python 3.10, the focused model tests pass on the minimum supported Pydantic 1.10.13 and on 1.10.26 (66 passed, 13 existing v2-only skips); Pydantic 2.12.5 passes all 79. Broader model and WebSocket suites pass with v1 (1,634 passed, 13 skipped) and v2 (1,647 passed). Ruff, Pyright, mypy, and the exact-commit custom-code budget pass. The shared model base is SDK-owned and absent from the current generated-file manifest. This change does not require regeneration.
Legacy `client.beta.realtime.connect()` inherited a 1 MiB native receive limit, so a larger event closed the connection with code 1009 unless callers already supplied `max_size=None`. Its synchronous option type also advertised `write_limit`, which only native async connections support. Default to no receive size limit when the beta connection opens, while keeping every caller value, including finite limits and zero. Use the existing synchronous options type for beta sync connections; the legacy async type and its supported write limit stay available. This changes only the SDK-owned beta path. Before the change, both default-large cases failed. Local native tests now pass on the minimum `websockets==13.0` (15/15) and `websockets==15.0.1` (46/46 including Realtime recovery controls). Tests compare the whole received payload, check code 1009 for caller limits, and verify that the beta sync type rejects the async-only literal while beta async still accepts it.
## Summary Adds optional IDs to stable and beta compaction trigger items. ## Changes - Accept omitted, null, or string compaction trigger IDs while retaining existing construction APIs.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Castiron custom codeEvaluated next: Trusted main policy:
public snapshot lineage does not contain the base generation |
|
Temporarily closing this sync while preserving its branch and commits. Validation caught two prerequisites: the existing next API differs from current main, and its legacy generation is absent from the main snapshot lineage. A normal Castiron preview also exposed formatter bootstrap choosing an incidental uv.lock instead of the explicitly Rye-managed project’s pinned formatter. I will address that narrow source issue first, then reconcile this transition through the supported generation/promotion workflow. No checks or metadata are being bypassed. |
Bring Python
nextup to currentmainbefore starting next-major changes. The final tree exactly matches main0e5794dc48b8f8d51f6047f8a0b1814eb1e65850; this carries existing reviewed SDK fixes, generated updates, dependency locks, security protections and Castiron tooling onto the stale branch.Resolved the temporary CI bridge against main's supported toolchain and removed
scripts/ci-bootstrapand the obsolete Nox configuration. Main already retired the old Stainless codeflow metadata; it stays retired. The custom-code policy is unchanged after its separate approved bootstrap in #4123. No new breaking-change profile is activated by this sync.Release creation remains main-only. Manual PyPI build/publish remain main-only, with protected release environments and publishing credentials unchanged. Next generation/protection setup and the Python compaction candidate follow separately.
Validation: exact tree identity with current main; reviewed merge resolutions and release guards; Python support-policy check passes. The identical main tree previously passed 95 focused Castiron tests (one skipped). Local trusted-main budget and isolation checks pass at 7,351/10,000 lines, verifying snapshot e23db48; the documented local Git I/O workaround changes only batch input transport. Fresh hosted CI and SDK-team review are required before merge. This is a baseline synchronization, so its large diff consists entirely of already-landed main changes.